<?php
//TODO: Config file
require_once(BASE_DIR . '/external/PasswordHash.php');
require_once(MODEL_DIR . '/Model.php');
require_once('User.php');
require_once('NoSuchUserException.php');
/**
 * 
 * The UserFactory class allows us to perform data queries on the data source in
 * order to perform CRUD operations on users. The entries in the data source are returned
 * as User objects, which provide several convience methods when working with the CMS.
 * 
 * //TODO: Add documentation about how users work with sessions, etc.
 * @author Rick Hutcheson
 *
 */
class UserFactory extends Model {
	
	/**
	 * 
	 * Creates a User object from a given username and (optionally)
	 * an array containing the user's values. 
	 * 
	 * @param string $username
	 * 	The username from which we create the User object.
	 * @param string $uservals
	 * 	The User values, returned from the data source, used to initialize the values
	 * 	of the User. If these values are not passed into the function, the function will
	 * 	attempt to populate them itself from the data source.
	 * 
	 * @throws NoSuchUserException
	 */
	private function createUserFromUsername($username, array $uservals = array()) {
		if(empty($uservals)) {
			$stmt = $this->database->prepare("SELECT * FROM USERS WHERE username = ?");
			$stmt->bindParam(1, $username);
			$stmt->execute();
			$uservals = $stmt->fetch(PDO::FETCH_ASSOC);
		}
		if(empty($uservals)) {
			throw new NoSuchUserException();
		} else {
			return new User($uservals['username'], $uservals['pass_hash'],
                            $uservals['hash_iterations'],$uservals['first_name'], 
                            $uservals['last_name'],$uservals['profile_pic']);
		}
	}
	
	
	/**
	 * 
	 * Returns the user associated with the specified ID. If no such user exists, 
	 * a NoSuchUserException is thrown.
	 *
	 * @param int $id
	 * 	The id of the user we wish returned.
	 * @throws NoSuchUserException
	 */
	public function getUserFromUsername($username) {
		$stmt = $this->database->prepare("SELECT * FROM users WHERE username = ?");
		$stmt->bindParam(1, $username);
		$stmt->execute();
		$result = $stmt->fetch(PDO::FETCH_ASSOC);
		
		if(empty($result)) {
			throw new NoSuchUserException();
		} else {
			return $this->createUserFromUsername($username, $result);
		}
	}
	
	/**
	 * 
	 * Attemps to authenticate as the specified user, thus allowing the caller to gain
	 * all privileges associated with the specified user.
	 * 
	 * @param string $username
	 * 	The username as whom we wish to authenticate. 
	 * @param string $password
	 * 	The password used to attempt authentication.
	 */
	public function attemptLogin($username, $password) {
		$stmt = $this->database->prepare("SELECT * FROM users WHERE username = ?");
		$stmt->bindParam(1, $username);
		$stmt->execute();
		$result = $stmt->fetch(PDO::FETCH_ASSOC);
		if(!$result) {
			throw new NoSuchUserException();
		} else {
			$iterations = $result['hash_iterations'];
			$hasher = new PasswordHash($iterations, FALSE);
			$hash = $hasher->HashPassword($password);
			//returns true if passwords match, else false
			return $hasher->CheckPassword($password, $result['pass_hash']);
		}
	}
	
	public function __construct() {
		parent::__construct();
	}
	public function __destruct() {
		parent::__destruct();
	}
	
	/**
	 * 
	 */
	public function createUser(User $user) {
		// username, pass_hash, hash_iterations, first_name, last_name, profile_pic
		$stmt = $this->database->prepare(
							"INSERT INTO USERS VALUES (	:username, :pass_hash, 
														:hash_iterations, :first_name, 
														:last_name, :profile_pic");
		
		// 2nd line of defense. Javascript failure.
		if (strlen($user->getUsername()) == 0) {
			throw new UserCreationException("username");
		} else if (strlen($user->getFirstName()) == 0) {
			throw new UserCreationException("first name");
		} else if (strlen($user->getLastName()) == 0) {
			throw new UserCreationException("last name");
		} else if (strlen($user->getPlainPassword()) == 0) {
			throw new UserCreationException("password");
		} else if (strlen($user->getProfilePicture()) == 0) {
			//TODO: Replace with some config setting
			$user->setProfilePicture("default.png");
		}
		
		$stmt->bindParam(':username', $user->getUsername());
		$stmt->bindParam(':pass_hash', $user->getPasswordHash());
		
		//CONFIG SETTING
		$stmt->bindParam(':hash_iterations', 8);
		$stmt->bindParam(':first_name', $user->getFirstName());
		$stmt->bindParam(':last_name', $user->getLastName());
		$stmt->bindParam(':profile_pic', $user->getProfilePicture());
		
		if(!$stmt->execute()) {
			throw new UserCreationException("database");
		}
	}
	
	private function hashInputPassword($password) {
		if(strlen($password) == 0) {
			throw new UserCreationException("password");
		}
		// Hash the password
		// TODO: CONFIG SETTING
		$hasher = new PasswordHash(8, FALSE);
		$hash = $hasher->HashPassword($password);
	
		// Check hashing.
		if($hash == '*') {
			throw new UserCreationException("Password unable to be hashed.");
		}
	}
	
	public function createUserFromInput($username, $password, $firstName, $lastName, 
										$profilePic) {
		
		try {
			$hash = $this->hashInputPassword($password);
		} catch(UserCreationException $uce) {
			throw $uce;
		}
		$this->createUser(new User($username, $hash, $firstName, $lastName, $profilePic));
	}
	
	public function updateUserFromInput($username, $password, $firstName, $lastName,
	$profilePic) {
		try {
			$hash = $this->hashInputPassword($password);
		} catch(UserCreationException $uce) {
			throw $uce;
		}
		$this->updateUser(new User($username, $hash, $firstName, $lastName, $profilePic));
	}
	
	
	public function updateUser(User $user) {
		// username, pass_hash, hash_iterations, first_name, last_name, profile_pic
		$stmt = $this->database->prepare(
									"UPDATE USERS set	pass_hash = :pass_hash,
														first_name 	= :first_name, 
														last_name 	= :last_name, 
														profile_pic = :profile_pic
									WHERE username = :username");
		
		// 2nd line of defense. Javascript failure.
		if (strlen($user->getUsername()) == 0) {
			throw new UserCreationException("username");
		} else if (strlen($user->getFirstName()) == 0) {
			throw new UserCreationException("first name");
		} else if (strlen($user->getLastName()) == 0) {
			throw new UserCreationException("last name");
		} else if (strlen($user->getProfilePicture()) == 0) {
			//TODO: Replace with some config setting
			$user->setProfilePicture("default.png");
		}
		
		$stmt->bindParam(':username', $user->getUsername());
		$stmt->bindParam(':pass_hash',$user->getPasswordHash());
		
		//CONFIG SETTING
		$stmt->bindParam(':hash_iterations', $user->getHashIterations());
		$stmt->bindParam(':first_name', $user->getFirstName());
		$stmt->bindParam(':last_name', $user->getLastName());
		$stmt->bindParam(':profile_pic', $user->getProfilePicture());
		
		if(!$stmt->execute()) {
			throw new UserCreationException("database");
		}
	}
	
	public function logout() {
		
	}
	
}
